ASP.NET Core 提供的 Logging API,不僅可以方便調用 Logger,且支援多種 Log 輸出,也能把 Log 發送到多個地方,並支援第三方的 Logging Framework 套件。
本篇將介紹 ASP.NET Core 的 Logging 搭配第三方 Logging Framework 套件,NLog 及 Log4net 的範例。
同步發佈至個人部落格:
John Wu's Blog - [鐵人賽 Day19] ASP.NET Core 2 系列 - NLog & Log4net
NLog 是 .NET 的熱門 Logging Framework;而且還是 ASP.NET Core 官方第三方 Logging Framework 推薦名單之一。
ASP.NET Core 使用 NLog 需要安裝 NLog
及 NLog.Web.AspNetCore
套件。
透過 .NET Core CLI 在專案資料夾執行安裝指令:
dotnet add package NLog -v 4.5.0-rc02
dotnet add package NLog.Web.AspNetCore -v 4.5.0-rc2
.NET Core 的版本目前還是 RC 版。
新增一個 nlog.config 的檔案如下:
nlog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog
xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="info"
internalLogFile="C:\Logs\MyWebsite\nlog-internal.txt">
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="ALL"
fileName="C:\Logs\MyWebsite\nlog-all_${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId.Id}|${uppercase:${level}}|${logger}|${message} ${exception}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="ALL" />
</rules>
</nlog>
NLog 組態設定可以參考:NLog Configuration file
在 Program.Main
啟動時載入 NLog 組態設定檔,並在 WebHost Builder 注入 NLog 服務。
Program.cs
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using NLog.Web;
namespace MyWebsite
{
public class Program
{
public static void Main(string[] args)
{
NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseNLog()
.Build();
}
}
}
Log 輸出範例延續前篇[Day18] ASP.NET Core 2 系列 - Logging
以下 Log 輸出都是以前篇的Home.Index()
做為範例。
輸出結果如下:
C:\Logs\MyWebsite\nlog-all_2018-01-07.log
2018-01-07 00:27:32.6339||INFO|Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager|User profile is available. Using 'C:\Users\john.wu\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
2018-01-07 00:27:33.1149||INFO|Microsoft.AspNetCore.Hosting.Internal.WebHost|Request starting HTTP/1.1 GET http://localhost:5000/
2018-01-07 00:27:33.1969||INFO|Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker|Executing action method MyWebsite.HomeController.Index (MyWebsite) with arguments ((null)) - ModelState is Valid
2018-01-07 00:27:33.1999||INFO|MyWebsite.HomeController|This information log from Home.Index()
2018-01-07 00:27:33.1999||WARN|MyWebsite.HomeController|This warning log from Home.Index()
2018-01-07 00:27:33.1999||ERROR|MyWebsite.HomeController|This error log from Home.Index()
2018-01-07 00:27:33.1999||FATAL|MyWebsite.HomeController|This critical log from Home.Index()
2018-01-07 00:27:33.2219||INFO|Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor|Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext.
2018-01-07 00:27:33.2459||INFO|Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker|Executed action MyWebsite.HomeController.Index (MyWebsite) in 56.8935ms
2018-01-07 00:27:33.2519||INFO|Microsoft.AspNetCore.Hosting.Internal.WebHost|Request finished in 137.5115ms 200 text/plain; charset=utf-8
安裝完套件後,只要加一個設定檔及兩行程式碼就完成,可說是非常的友善使用。
從網路上各方訊息看來,Log4net 應該是 .NET 最熱門的 Logging Framework,我個人也是習慣用 Log4net。
.NET Core 使用 Log4net 需要安裝 log4net
套件。
透過 .NET Core CLI 在專案資料夾執行安裝指令:
dotnet add package log4net
新增一個 log4net.config 的檔案如下:
log4net.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>
<appender name="All" type="log4net.Appender.RollingFileAppender">
<file value="C:\Logs\MyWebsite\log4net-all" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<datePattern value="_yyyy-MM-dd.lo\g" />
<maximumFileSize value="5MB" />
<maxSizeRollBackups value="15" />
<staticLogFileName value="false" />
<PreserveLogFileNameExtension value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger - %message%newline" />
</layout>
</appender>
<root>
<appender-ref ref="All" />
</root>
</log4net>
</configuration>
Log4net 組態設定可以參考:Apache log4net Manual - Configuration
在 Program.Main
啟動時載入 Log4net 組態設定檔。
Program.cs
using System.IO;
using System.Reflection;
using log4net;
using log4net.Config;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace MyWebsite
{
public class Program
{
private readonly static ILog _log = LogManager.GetLogger(typeof(Program));
public static void Main(string[] args)
{
LoadLog4netConfig();
_log.Info("Application Start");
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
private static void LoadLog4netConfig()
{
var repository = LogManager.CreateRepository(
Assembly.GetEntryAssembly(),
typeof(log4net.Repository.Hierarchy.Hierarchy)
);
XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));
}
}
}
載入 Log4net 組態設定後,就可以直接操作 _log
物件寫 Log,用法就跟過去 .NET Framework 一樣。
但 Log4net 沒有實作 ASP.NET Core 的 Logging API,所以沒辦法透過注入 ILogger
寫 Log4net 的 Log。
難怪 ASP.NET Core 官方不推 Log4net...
既然 Log4net 沒有實作 ILogger
,就自己做吧!
建立一個 Log4netLogger.cs,內容如下:
Log4netLogger.cs
using System;
using System.IO;
using System.Reflection;
using log4net;
using log4net.Config;
using Microsoft.Extensions.Logging;
namespace MyWebsite
{
public class Log4netLogger : ILogger
{
private readonly ILog _log;
public Log4netLogger(string name, FileInfo fileInfo)
{
var repository = LogManager.CreateRepository(
Assembly.GetEntryAssembly(),
typeof(log4net.Repository.Hierarchy.Hierarchy)
);
XmlConfigurator.Configure(repository, fileInfo);
_log = LogManager.GetLogger(repository.Name, name);
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
switch (logLevel)
{
case LogLevel.Critical: return _log.IsFatalEnabled;
case LogLevel.Debug:
case LogLevel.Trace: return _log.IsDebugEnabled;
case LogLevel.Error: return _log.IsErrorEnabled;
case LogLevel.Information: return _log.IsInfoEnabled;
case LogLevel.Warning: return _log.IsWarnEnabled;
default:
throw new ArgumentOutOfRangeException(nameof(logLevel));
}
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
string message = null;
if (null != formatter)
{
message = formatter(state, exception);
}
if (!string.IsNullOrEmpty(message) || exception != null)
{
switch (logLevel)
{
case LogLevel.Critical: _log.Fatal(message); break;
case LogLevel.Debug:
case LogLevel.Trace: _log.Debug(message); break;
case LogLevel.Error: _log.Error(message); break;
case LogLevel.Information: _log.Info(message); break;
case LogLevel.Warning: _log.Warn(message); break;
default:
_log.Warn($"Unknown log level {logLevel}.\r\n{message}");
break;
}
}
}
}
}
由於 Log4net 的 Log Level 跟 ASP.NET Core Logger API 的級別不一致,所以要將 Log Level 的事件做相對的對應。
ILogger
主要是透過 Logger Provider 產生,所以需要實作 ILoggerProvider
。
建立一個 Log4netProvider.cs,內容如下:
Log4netProvider.cs
using System.IO;
using Microsoft.Extensions.Logging;
namespace MyWebsite
{
public class Log4netProvider : ILoggerProvider
{
private readonly FileInfo _fileInfo;
public Log4netProvider(string log4netConfigFile)
{
_fileInfo = new FileInfo(log4netConfigFile);
}
public ILogger CreateLogger(string name)
{
return new Log4netLogger(name, _fileInfo);
}
public void Dispose()
{
}
}
}
將 Log4netProvider
註冊到 WebHost 的 ConfigureLogging
中。
Program.cs
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
namespace MyWebsite
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(logging => {
logging.AddProvider(new Log4netProvider("log4net.config"));
})
.UseStartup<Startup>()
.Build();
}
}
}
如此一來,也能透過 ASP.NET Core 的 Logger API 寫出 Log4net 的 Log 了。
輸出結果如下:
C:\Logs\MyWebsite\log4net-all_2018-01-07.log
2018-01-07 00:56:46,673 [1] INFO Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager - User profile is available. Using 'C:\Users\john.wu\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
2018-01-07 00:56:47,167 [17] INFO Microsoft.AspNetCore.Hosting.Internal.WebHost - Request starting HTTP/1.1 GET http://localhost:5000/
2018-01-07 00:56:47,261 [17] INFO Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker - Executing action method MyWebsite.HomeController.Index (MyWebsite) with arguments ((null)) - ModelState is Valid
2018-01-07 00:56:47,265 [17] INFO MyWebsite.HomeController - This information log from Home.Index()
2018-01-07 00:56:47,266 [17] WARN MyWebsite.HomeController - This warning log from Home.Index()
2018-01-07 00:56:47,268 [17] ERROR MyWebsite.HomeController - This error log from Home.Index()
2018-01-07 00:56:47,269 [17] FATAL MyWebsite.HomeController - This critical log from Home.Index()
2018-01-07 00:56:47,278 [17] INFO Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext.
2018-01-07 00:56:47,303 [17] INFO Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker - Executed action MyWebsite.HomeController.Index (MyWebsite) in 52.7449ms
2018-01-07 00:56:47,305 [17] INFO Microsoft.AspNetCore.Hosting.Internal.WebHost - Request finished in 141.4295ms 200 text/plain; charset=utf-8
Introduction to Logging in ASP.NET Core
NLog - Getting started with ASP.NET Core 2
How to use Log4Net with ASP.NET Core for logging